﻿namespace IdGen;

/// <summary>
/// 指定为 <see cref="IdGenerator"/> 的 Id 的不同部分所使用的位数
/// </summary>
public class IdStructure
{
    /// <summary>
    /// 获取要用于生成的标识符（Id）的时间戳部分的位数
    /// </summary>
    public byte TimestampBits { get; private set; }

    /// <summary>
    /// 获取要用于生成的标识符（Id）的生成器标识（generator-id）部分的位数
    /// </summary>
    public byte GeneratorIdBits { get; private set; }

    /// <summary>
    /// 获取要用于生成的标识符（Id）的序列号部分的位数
    /// </summary>
    public byte SequenceBits { get; private set; }

    /// <summary>
    /// 返回此 <see cref="IdStructure"/> 配置的最大间隔数
    /// </summary>
    public long MaxIntervals => 1L << TimestampBits;

    /// <summary>
    /// 返回此 <see cref="IdStructure"/> 配置可用的生成器的最大数量
    /// </summary>
    public int MaxGenerators => 1 << GeneratorIdBits;

    /// <summary>
    /// 返回一个时间间隔内可生成的顺序标识符（Id）的最大数量（例如，在单个时间间隔内生成的标识符的最大数量）
    /// </summary>
    public int MaxSequenceIds => 1 << SequenceBits;

    /// <summary>
    /// 获取一个默认的 <see cref="IdStructure"/>，其标识符（id）的时间戳部分为 41 位，生成器标识（generator-id）部分为 10 位，序列号部分为 12 位
    /// </summary>
    public static IdStructure Default => new(41, 10, 12);

    /// <summary>
    /// 为 <see cref="IdGenerator"/> 初始化一个 <see cref="IdStructure"/>
    /// </summary>
    /// <param name="timestampBits">用于标识符（Id）的时间戳部分的位数</param>
    /// <param name="generatorIdBits">用于标识符（Id）的生成器标识部分的位数</param>
    /// <param name="sequenceBits">用于标识符（Id）的序列号部分的位数</param>
    public IdStructure(byte timestampBits, byte generatorIdBits, byte sequenceBits)
    {
        if (timestampBits + generatorIdBits + sequenceBits != 63)
        {
            throw new InvalidOperationException("用于生成标识符（Id）的位数不等于 63");
        }

        if (generatorIdBits > 31)
        {
            throw new ArgumentOutOfRangeException(nameof(generatorIdBits), "生成器标识（GeneratorId）的位数不能超过 31 位");
        }

        if (sequenceBits > 31)
        {
            throw new ArgumentOutOfRangeException(nameof(sequenceBits), "序列号的位数不能超过 31 位");
        }

        TimestampBits = timestampBits;
        GeneratorIdBits = generatorIdBits;
        SequenceBits = sequenceBits;
    }

    /// <summary>
    /// 针对给定的 <see cref="IdStructure"/>，计算在标识符（Id）的时间戳部分出现 “回绕” 情况之前，某个标识符对应的最后日期
    /// </summary>
    /// <param name="epoch"><see cref="IdGenerator"/>用作偏移量的已使用的时间纪元</param>
    /// <param name="timeSource"><see cref="IdGenerator"/>所使用的 <see cref="ITimeSource"/></param>
    /// <returns>在标识符（Id）的时间戳部分出现 “回绕” 情况之前，该标识符对应的最后日期</returns>
    /// <remarks>
    /// 请注意，对于超过 <see cref="DateTimeOffset.MaxValue"/> 的日期，将会抛出 <see cref="ArgumentOutOfRangeException"/>
    /// </remarks>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public DateTimeOffset WraparoundDate(DateTimeOffset epoch, ITimeSource timeSource) => timeSource == null
            ? throw new ArgumentNullException(nameof(timeSource))
            : epoch.AddDays(timeSource.TickDuration.TotalDays * MaxIntervals);

    /// <summary>
    /// 针对给定的 <see cref="IdStructure"/>，计算标识符（Id）的时间戳部分将出现 “回绕” 情况的时间间隔
    /// </summary>
    /// <param name="timeSource"><see cref="IdGenerator"/>所使用的 <see cref="ITimeSource"/></param>
    /// <returns>
    /// 对于给定的 <see cref="IdStructure"/>，标识符（Id）的时间戳部分将出现 “回绕” 情况的时间间隔
    /// </returns>
    /// <remarks>
    /// 请注意，对于超过 <see cref="TimeSpan.MaxValue"/> 的时间间隔，将会抛出 <see cref="OverflowException"/>
    /// </remarks>
    /// <exception cref="ArgumentNullException">
    /// Thrown when <paramref name="timeSource"/> is null.
    /// </exception>
    /// <exception cref="OverflowException"></exception>
    public TimeSpan WraparoundInterval(ITimeSource timeSource) => timeSource == null
            ? throw new ArgumentNullException(nameof(timeSource))
            : TimeSpan.FromDays(timeSource.TickDuration.TotalDays * MaxIntervals);
}